/* Autostop
*
* Copyright (c) Maciej Gajewski, Grzegorz Latarowski
* http://code.google.com/p/autostop-automaty-drogowe
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation; either version 2 of the License, or (at your
* option) any later version. NO WARRANTY OF ANY KIND is expressed or implied.
*/

// libc
#include <stdio.h>

// Qt
#include <QCoreApplication>
#include <QThread>
#include <QTcpServer>
#include <QTcpSocket>
#include <QDataStream>

// local
#include "camera.h"

// globals

quint16 	port	= 5102;							///< tcp port

// =============================== thread class ===================
class ServerThread : public QThread
{
private:
	int	_socket;	///< socket handle
	
public:

	// ---------------------- constructor ---------------------------
	ServerThread( int socket, QObject* parent ) : QThread( parent ), _socket( socket )
	{
	}
	virtual ~ServerThread() {}

	// ---------------------------- run --------------------------------
	virtual void run()
	{
		QTcpSocket socket;
		socket.setSocketDescriptor( _socket );
		// say something
		qDebug("Incomming connection from %s", qPrintable( socket.peerAddress().toString() ) );


		// init
		int imagewidth =  camera_image_width();
		int imageheight = camera_image_height();
		
		if ( imageheight <= 0 || imagewidth <= 0 )
		{
			qDebug("image size invalid");
			return;
		}
		QByteArray buffer;
		buffer.reserve( imagewidth* imageheight );
		QByteArray outBuffer;
		outBuffer.reserve( imagewidth* imageheight + 100 ); // roughly 100 bytes for extra data


		// serve:

		// wait for request
		// TODO not needed:
		//int requestSize = sizeof (quint32 ); // yes, i know this is 4 bytes, but...
		
		forever
		{
			if ( socket.bytesAvailable() == 0 )
			{
				//qDebug("waiting for data");
				if ( ! socket.waitForReadyRead( -1 ) )
				{
					qDebug("reading for request failed: %s", qPrintable( socket.errorString() ) );
					socket.close();
					return; // stop
				}
			}
			
			// read request
			quint32 request = 0;
			
			QDataStream stream( &socket );
			stream >> request;
			
			// get image from library
			uint64_t timestamp = 0;
			int width = 0;
			int height = 0;
			
			// request full Y image, request = 1
			if ( request == 1 )
			{
				width = imagewidth;
				height = imageheight;
				buffer.resize( width * height );
				int state = camera_get_image( (uint8_t*)buffer.data(), imagewidth * imageheight, &timestamp );
				if ( state != CAMERA_OK )
				{
					qDebug("error getting image: %s", camera_error_message() );
					return;
				}
			}
			// request segmented image, request = 2
			else if ( request == 2 )
			{
				width = imagewidth >> 3;
				height = imageheight >> 3;
				buffer.resize( width * height );
				int state = camera_get_segmented_image( (uint8_t*)buffer.data(), width * height, &timestamp );
				if ( state != CAMERA_OK )
				{
					qDebug("error getting segmented image: %s", camera_error_message() );
					return;
				}
			}
			else
			{
				qDebug("Unknown request: %d", request );
			}
			
			// write image
			outBuffer.clear();
			QDataStream outStream( &outBuffer, QIODevice::WriteOnly );
			outStream << width;
			outStream << height;
			outStream << buffer;
			
			// wrtie stream size
			stream << outBuffer.size();
			if ( ! socket.waitForBytesWritten( -1 ) )
			{
				qDebug("Error writing data: %s", qPrintable( socket.errorString() ) );
				return;
			}
			
			// wrtie out stream to socket
			int bytesWriten = 0;
			int bytesToWrite = outBuffer.size();
			while( bytesToWrite > 0 )
			{
				int w = socket.write( outBuffer.data() + bytesWriten, bytesToWrite );
				if ( ! socket.waitForBytesWritten( -1 ) )
				{
					qDebug("Error writing data: %s", qPrintable( socket.errorString() ) );
					return;
				}
				bytesToWrite -= w;
				bytesWriten += w;
			}
			
		} // forever
		
	}
	
};

// ============================== server class ====================
class Server : protected QTcpServer
{
public:

	Server() : QTcpServer() {}
	virtual ~Server() {}

	// ---------------- listen ----------------------
	/// Listen for incoming connection infinitely (or unitl error)
	void listen( quint16 port )
	{
		QTcpServer::listen( QHostAddress::Any, port );

		while ( waitForNewConnection(-1) )
		{
			// nothing, processing in incomingConnection()
		}
	}

	// ------------------- incoming connection -------
	virtual void incomingConnection( int socket )
	{
		ServerThread *thread = new ServerThread( socket, this );
		connect(thread, SIGNAL(finished()), thread, SLOT(deleteLater()));
		thread->start();
	}
};

// ================================ usage ================================
void usage()
{
	fprintf( stderr, "Simple server, listens on TCP port %d, on all interfaces\n", (int)port );
}

// ================================ main ================================-
int main( int argc, char** argv )
{
	QCoreApplication app( argc, argv );
	
	// create server
	Server server;

	// init camera
	int state = camera_init();
	if ( state != CAMERA_OK )
	{
		qDebug("Error initializing camera: %s", camera_error_message() );
		return 1;
	}

	// listen, process
	qDebug("Starting test server: %s %s", __DATE__, __TIME__ );
	server.listen( port );

	return 0;
}



// eof

